/*

Copyright (C) 2000 - 2008 Christian Kreibich <christian@whoop.org>.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies of the Software and its documentation and acknowledgment shall be
given in the documentation and software packages that this Software was
used.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

// #include <nd.h>
#include <nd_dialog.h>
#include <nd_protocol.h>
#include <nd_protocol_inst.h>
#include <nd_gui.h>

ND_Protocol   *
nd_proto_new(LND_Protocol *proto)
{
	ND_Protocol *proto_gui;
	
	if (!proto)
		return NULL;
	
	proto_gui = g_new0(ND_Protocol, 1);
	if (! proto_gui)
		return NULL;
	
	libnd_reg_set_data(proto->registry, "proto_gui", proto_gui);
	
	return proto_gui;
}


void
nd_proto_free(LND_Protocol *proto)
{
	ND_Protocol *proto_gui;
	
	if (!proto)
		return;
	
	if ( (proto_gui = libnd_reg_del_data(proto->registry, "proto_gui")))
		g_free(proto_gui);
	
	/* FIXME -- is this properly cleaned up? */
	
	libnd_proto_free(proto);
}


ND_Protocol      *
nd_proto_get(const LND_Protocol *proto)
{
	ND_Protocol *proto_gui;
	
	if (!proto)
		return NULL;
	
	proto_gui = libnd_reg_get_data(proto->registry, "proto_gui");
	D_ASSERT_PTR(proto_gui);
	
	return proto_gui;
}


ND_ProtoField *
nd_proto_field_duplicate(ND_ProtoField *field)
{
	ND_ProtoField *result;
	
	result = g_new(ND_ProtoField, 1);
	
	if (!result)
		return NULL;
	
	memcpy(result, field, sizeof(ND_ProtoField));
	
	return result;
}


void              
nd_proto_field_free(ND_ProtoField *field)
{
	g_free(field);
}


void
nd_proto_field_set(const LND_ProtoInfo *pinf,
		   ND_ProtoField *field,
		   void *data)
{
	GtkWidget     *button;
	char           s[MAXPATHLEN];
	
	if (!pinf || !field)
		return;
	
	button = libnd_reg_get_data(pinf->registry, nd_proto_field_to_string(field));
	D_ASSERT_PTR(button);
	
	switch (field->type)
	{
		case ND_VAL_FIELD:
			g_snprintf(s, MAXPATHLEN, field->label, data);      
			gtk_label_set_text(GTK_LABEL(GTK_BIN(button)->child), s);
			break;
	
		case ND_FLG_FIELD:
			if (GPOINTER_TO_INT(data))
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
			else
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
			break;
	
		default:
			D(("Warning -- invalid type in header field!"));
	}
}


void
nd_proto_field_set_for_menu(const LND_ProtoInfo *pinf,
			    ND_ProtoField *field,
			    void *data,
			    ND_MenuData *menu,
			    char *alt_form_str)
{
	GtkWidget     *button;
	char           s[MAXPATHLEN];
	char           s2[MAXPATHLEN];
	int            i;
	gboolean       found_value = FALSE;
	
	if (!pinf || !field || !menu || !alt_form_str)
		return;
	
	if (field->type != ND_VAL_FIELD)
	{
		nd_proto_field_set(pinf, field, data);
		return;
	}
	
	for (i = 0; menu[i].label; i++)
	{
		if (GPOINTER_TO_INT(data) == menu[i].value)
		{
			g_snprintf(s2, MAXPATHLEN, field->label, menu[i].label);
			found_value = TRUE;
			break;
		}
	}
	
	if (!found_value)
	{
		g_snprintf(s, MAXPATHLEN, alt_form_str, data);
		g_snprintf(s2, MAXPATHLEN, field->label, s);
	}
	
	button = libnd_reg_get_data(pinf->registry, nd_proto_field_to_string(field));
	D_ASSERT_PTR(button);
	gtk_label_set_text(GTK_LABEL(GTK_BIN(button)->child), s2);
}


const char *
nd_proto_field_to_string(const ND_ProtoField *field)
{
	static char key[MAXPATHLEN];
	
	g_snprintf(key, MAXPATHLEN, "field_%p", field);
	return key;
}


char *
nd_proto_get_opt_key(LND_Protocol *proto)
{
  static char key[MAXPATHLEN];

  g_snprintf(key, MAXPATHLEN, "%s_opt_fields", proto->name);
  return key;
}


static void
proto_4bit_lo_ok_cb(LND_Packet *packet, void *user_data, guint value)
{
	guchar                val8;
	guchar               *data = (guchar*) user_data;
	
	val8 = (guchar) value;
	
	*data &= 0xF0;
	*data |= (val8 & 0x0F);
	libnd_packet_modified(packet);
}


void
nd_proto_4bit_lo_cb(LND_Packet   *packet,
		    guchar      *header,
		    guchar      *data)
{
	nd_dialog_number(_("Enter 4-bit data:"),
			ND_BASE_DEC,
			(*data & 0x0F), 15,
			proto_4bit_lo_ok_cb,
			NULL,
			packet, data);
	
	return;
	TOUCH(header);
}


static void
proto_4bit_hi_ok_cb(LND_Packet *packet, void *user_data, guint value)
{
	guchar                val8;
	guchar               *data = (guchar*) user_data;
	
	val8 = (guchar) value;
	
	*data &= 0x0F;
	*data |= ((val8 & 0x0F) << 4); 
	libnd_packet_modified(packet);
}


void
nd_proto_4bit_hi_cb(LND_Packet   *packet,
		    guchar      *header,
		    guchar      *data)
{
	nd_dialog_number(_("Enter 4-bit data:"),
			ND_BASE_DEC,
			(*data & 0xF0), 15,
			proto_4bit_hi_ok_cb,
			NULL,
			packet, data);
	
	return;
	TOUCH(header);
}


static void
proto_8bit_ok_cb(LND_Packet *packet, void *user_data, guint value)
{
	guchar               *data = (guchar*) user_data;
	
	*data = (guchar) value;
	libnd_packet_modified(packet);
}


void
nd_proto_8bit_cb(LND_Packet   *packet,
		 guchar      *header,
		 guchar      *data)
{
	nd_dialog_number(_("Enter 8-bit data:"),
			ND_BASE_DEC,
			*data, 255,
			proto_8bit_ok_cb,
			NULL,
			packet, data);
	
	return;
	TOUCH(header);
}


static void
proto_16bit_ok_cb(LND_Packet *packet, void *user_data, guint value)
{
	guint16 *data = (guint16*) user_data;
	
	*data = htons((guint16) value);
	libnd_packet_modified(packet);
}


void
nd_proto_16bit_cb(LND_Packet   *packet,
		  guchar      *header,
		  guchar      *data)
{
	guint16 *data16 = (guint16 *) data;
	
	nd_dialog_number(_("Enter 16-bit data:"),
			ND_BASE_HEX,
			ntohs(*data16), 65535,
			proto_16bit_ok_cb,
			NULL,
			packet, data);
	
	return;
	TOUCH(header);
}


static void
proto_24bit_ok_cb(LND_Packet *packet, void *user_data, guint value)
{
	guchar *val_p = (guchar *) &value;
	guchar *data = (guchar *) user_data;
	
#ifdef WORDS_BIGENDIAN
	data[0] = val_p[0];
	data[1] = val_p[1];
	data[2] = val_p[2];
#else
	data[0] = val_p[2];
	data[1] = val_p[1];
	data[2] = val_p[0];
#endif
	
	libnd_packet_modified(packet);
}

void
nd_proto_24bit_cb(LND_Packet   *packet,
		  guchar      *header,
		  guchar      *data)
{
	guint val = 0;
	
#ifdef WORDS_BIGENDIAN
	val = (data[2] << 16 | data[1] << 8 | data[0]);
#else
	val = (data[0] << 16 | data[1] << 8 | data[2]);
#endif
	
	nd_dialog_number(_("Enter 32-bit timestamp:"),
			ND_BASE_HEX,
			val, (1 << 23) - 1,
			proto_24bit_ok_cb,
			NULL,
			packet, data);
	
	return;
	TOUCH(header);
}


static void
proto_32bit_ok_cb(LND_Packet *packet, void *user_data, guint value)
{
	guint32 *data = (guint32 *) user_data;
	
	*data = htonl((guint32) value);
	libnd_packet_modified(packet);
}


void
nd_proto_32bit_cb(LND_Packet   *packet,
		  guchar      *header,
		  guchar      *data)
{
	guint32 *data32 = (guint32 *) data;
	
	nd_dialog_number(_("Enter 32-bit data:"),
			ND_BASE_DEC,
			ntohl(*data32), (guint) -1,
			proto_32bit_ok_cb,
			NULL,
			packet, data);
	
	return;
	TOUCH(header);
}


ND_ProtoCallbackData *
nd_proto_cb_data_new(LND_Protocol *proto,
		     guchar *data,
		     guint offset,
		     gboolean update_packet)
{
	ND_ProtoCallbackData *cb_data;
				
	if (!proto)
		return NULL; 
	
	cb_data = g_new0(ND_ProtoCallbackData, 1);
	D_ASSERT_PTR(cb_data);
	
	if (!cb_data)
		return NULL;
	
	cb_data->proto         = proto;
	cb_data->data          = data;
	cb_data->offset        = offset;
	cb_data->update_packet = update_packet;
	
	return cb_data;
}


void
nd_proto_cb_data_free(ND_ProtoCallbackData *cb_data)
{
	g_free(cb_data);
}


static void   
proto_iterate_impl(LND_Packet *packet,
		   void       *user_data,
		   guint       value,
		   ND_ProtoCallbackMode mode)
{
	LND_Trace            *trace;
	LND_PacketIterator    pit;
	ND_ProtoCallbackData *data = (ND_ProtoCallbackData *) user_data;
	int                   nesting; 
	guchar               *data_ptr;
	
	D_ENTER;
	
	if (!packet || !user_data || !data || !data->proto)
		D_RETURN;
	
	D(("Generic callback -- proto %s, offset %u, value %lu, updating: %i, mode %i\n",
			data->proto->name, data->offset, (long unsigned) value,
			data->update_packet, mode));
	
	nesting = libnd_packet_get_proto_nesting(packet, data->proto, data->data);
	trace = libnd_packet_get_trace(packet);
	
	if (nesting < 0 || !trace)
	{
		D(("Invalid input -- aborting.\n"));
		D_RETURN;
	}
	
	for (libnd_pit_init(&pit, trace); libnd_pit_get(&pit); libnd_pit_next(&pit))
	{
		if (! (data_ptr = libnd_packet_get_data(libnd_pit_get(&pit), data->proto, nesting)))
			continue;
	
		switch (mode)
		{
			case ND_PROT_CB_4LO:
				*(data_ptr + data->offset) &= 0xF0;
				*(data_ptr + data->offset) |= ((((guchar) value) & 0x0F));
				break;
				
			case ND_PROT_CB_4HI:
				*(data_ptr + data->offset) &= 0x0F;
				*(data_ptr + data->offset) |= ((((guchar) value) & 0x0F) << 4);
				break;
				
			case ND_PROT_CB_8:
				*(data_ptr + data->offset) = (guchar) value;
				break;
				
			case ND_PROT_CB_16:
				*((guint16*) (data_ptr + data->offset)) = htons((guint16) value);
				break;
				
			case ND_PROT_CB_32:
				*((guint32*) (data_ptr + data->offset)) = htonl((guint32) value);
				break;
				
			default:
				D(("Invalid callback data type %i\n", data->mode));
		}
		
		if (data->update_packet)
			libnd_packet_update(libnd_pit_get(&pit), data->proto, data->nesting);
	
		libnd_packet_modified(libnd_pit_get(&pit));
	}
	
// 	nd_gui_list_update(trace);
	D_RETURN;
	TOUCH(value);
}


void   
nd_proto_iterate_4bit_lo_cb(LND_Packet *packet,
			    void      *user_data,
			    guint      value)
{
	proto_iterate_impl(packet, user_data, value, ND_PROT_CB_4LO);
}


void   
nd_proto_iterate_4bit_hi_cb(LND_Packet *packet,
			    void      *user_data,
			    guint      value)
{
	proto_iterate_impl(packet, user_data, value, ND_PROT_CB_4HI);
}


void   
nd_proto_iterate_8bit_cb(LND_Packet *packet,
			 void      *user_data,
			 guint      value)
{
	proto_iterate_impl(packet, user_data, value, ND_PROT_CB_8);
}


void   
nd_proto_iterate_16bit_cb(LND_Packet *packet,
			  void      *user_data,
			  guint      value)
{
	proto_iterate_impl(packet, user_data, value, ND_PROT_CB_16);
}


void   
nd_proto_iterate_32bit_cb(LND_Packet *packet,
			  void      *user_data,
			  guint      value)
{
	proto_iterate_impl(packet, user_data, value, ND_PROT_CB_32);
}
